//LIBTCOD is (c) J.C.Wilk (http://jice.nospam.googlepages.com/)
//LIBTCOD_D PORT is a work by W."Uiriamu" F.M
//Kept as faithful to original as possible.
//Licensed under BSD license as the original libtcod.
//Provided as free download at http://code.google.com/p/ariayakita

version(Windows) {
    import std.c.windows.windows;
}
import std.math;
import libtcod_d;

extern(C):
//! Starts bresenham
//!-----------------------------------------------------------------------------------------------------
int stepx;
int stepy;
int e;
int deltax;
int deltay;
int destx;
int desty;
//TCOD_line_init(int, int, int, int);
void TCOD_line_init(int xFrom, int yFrom, int xTo, int yTo) {
    int x=xFrom,y=yFrom;
    destx=xTo;
    desty=yTo;
    deltax=destx-x;
    deltay=desty-y;
    if ( deltax > 0 ) {
        stepx=1;
    } else if ( deltax < 0 ) {
        stepx=-1;
    } else stepx=0;
    if ( deltay > 0 ) {
        stepy=1;
    } else if ( deltay < 0 ) {
        stepy=-1;
    } else stepy = 0;
    if ( stepx*deltax > stepy*deltay ) {
        e = stepx*deltax;
        deltax *= 2;
        deltay *= 2;
    } else {
        e = stepy*deltay;
        deltax *= 2;
        deltay *= 2;
    }
}
//Advance one step. Returns true if we reach destination.
bool TCOD_line_step(int *xCur, int *yCur) {
    if ( stepx*deltax > stepy*deltay ) {
        if ( (*xCur) == destx ) return true;
        (*xCur)+=stepx;
        e -= stepy*deltay;
        if ( e < 0) {
            (*yCur)+=stepy;
            e+=stepx*deltax;
        }
    } else {
        if ( (*yCur) == desty ) return true;
        (*yCur)+=stepy;
        e -= stepx*deltax;
        if ( e < 0) {
            (*xCur)+=stepx;
            e+=stepy*deltay;
        }
    }
    return false;
}
//! Starts color
//!-----------------------------------------------------------------------------------------------------
bool TCOD_color_equals (TCOD_color_t c1, TCOD_color_t c2) {
    return (c1.r == c2.r && c1.g == c2.g && c1.b == c2.b);
}

TCOD_color_t TCOD_color_add(TCOD_color_t c1, TCOD_color_t c2) {
    TCOD_color_t ret;
    int r,g,b;
    r = cast(int)c1.r + cast(int)c2.r;
    g = cast(int)c1.g + cast(int)c2.g;
    b = cast(int)c1.b + cast(int)c2.b;
    r=MIN(255,r);
    g=MIN(255,g);
    b=MIN(255,b);
    r=MAX(0,r);
    g=MAX(0,g);
    b=MAX(0,b);
    ret.r=cast(uint8)r;
    ret.g=cast(uint8)g;
    ret.b=cast(uint8)b;
    return ret;
}

TCOD_color_t TCOD_color_multiply (TCOD_color_t c1, TCOD_color_t c2) {
    TCOD_color_t ret;
    ret.r=cast(uint8)((cast(int)c1.r)*(cast(int)(c2.r/255)));
    ret.g=cast(uint8)((cast(int)c1.g)*(cast(int)(c2.g/255)));
    ret.b=cast(uint8)((cast(int)c1.b)*(cast(int)(c2.b/255)));
    return ret;
}

TCOD_color_t TCOD_color_multiply_scalar (TCOD_color_t c1, float value) {
    TCOD_color_t ret;
    int r,g,b;
    r = cast(int)(c1.r * value);
    g = cast(int)(c1.g * value);
    b = cast(int)(c1.b * value);
    r=MIN(255,r);
    g=MIN(255,g);
    b=MIN(255,b);
    r=MAX(0,r);
    g=MAX(0,g);
    b=MAX(0,b);
    ret.r=cast(uint8)r;
    ret.g=cast(uint8)g;
    ret.b=cast(uint8)b;
    return ret;
}

TCOD_color_t TCOD_color_lerp(TCOD_color_t c1, TCOD_color_t c2, float coef) {
    TCOD_color_t ret;
    ret.r=cast(uint8)((cast(int)(c1.r))+((cast(int)c2.r)-cast(int)(c1.r))*coef);
    ret.g=cast(uint8)((cast(int)(c1.g))+((cast(int)c2.g)-cast(int)(c1.g))*coef);
    ret.b=cast(uint8)((cast(int)(c1.b))+((cast(int)c2.b)-cast(int)(c1.b))*coef);
    return ret;
}

//Jice left this note on libtcod so I am leaving it as well:
//0<= h < 360, 0 <= s <= 1, 0 <= v <= 1
void TCOD_color_set_HSV(TCOD_color_t *c, float h, float s, float v) {
    int i;                                    //                 ^
    float f, p, q, t;                         //  thought        |
                                              //     this        |
    if ( s == 0 ) {                           //  would b cool!  |
        // achromatic (grey)                                     |
        c.r = c.g = c.b = cast(uint8)(v*255); //-----------------+
        return;
    }

    h /= 60;			// sector 0 to 5 ( h = h/60 ? o_o) uncommon operator use! learning everyday.
    i = cast(int)floor( h );
    f = h - i;			// factorial part of h
    p = v * ( 1 - s );
    q = v * ( 1 - s * f );
    t = v * ( 1 - s * ( 1 - f ) );

    switch ( i ) {
    case 0:
        c.r = cast(uint8)(v*255); //D was not made for casting. And I do have tedious typing when
        c.g = cast(uint8)(t*255); //we do have to cast types.
        c.b = cast(uint8)(p*255);
        break;
    case 1:
        c.r = cast(uint8)(q*255);
        c.g = cast(uint8)(v*255);
        c.b = cast(uint8)(p*255);
        break;
    case 2:
        c.r = cast(uint8)(p*255);
        c.g = cast(uint8)(v*255);
        c.b = cast(uint8)(t*255);
        break;
    case 3:
        c.r = cast(uint8)(p*255);
        c.g = cast(uint8)(q*255);
        c.b = cast(uint8)(v*255);
        break;
    case 4:
        c.r = cast(uint8)(t*255);
        c.g = cast(uint8)(p*255);
        c.b = cast(uint8)(v*255);
        break;
    default:
        c.r = cast(uint8)(v*255);
        c.g = cast(uint8)(p*255);
        c.b = cast(uint8)(q*255);
        break;
    }
}

//                                                  ^
//                                                  |   TCOD_color_set_HSV
//This is the infamous get version of our set from -+
//       Ok I am just making a big deal of it. >_>
void TCOD_color_get_HSV(TCOD_color_t c, float *h, float *s, float *v) {
    uint8 imax,imin;
    float min, max, delta;

    imax = ( c.r > c.g ? ( c.r > c.b ? c.r : c.b ) : ( c.g > c.b ? c.g : c.b) );
    imin = ( c.r < c.g ? ( c.r < c.b ? c.r : c.b ) : ( c.g < c.b ? c.g : c.b) );
    max = imax/255.0f;
    min = imin/255.0f;
    *v = max; // v

    delta = max - min;
    if ( max != 0 ) *s = delta / max; // s
    else {
        *s = 0; // s
        *h= -1; // h
        return;
    }

    if ( c.r == imax ) *h = ( c.g - c.b ) / (255 * delta);		// between yellow & magenta
    else if ( c.g == imax )	*h = 2 + ( c.b - c.r ) / (255 * delta);	// between cyan & yellow
    else *h = 4 + ( c.r - c.g ) / (255 * delta);	// between magenta & cyan

    *h *= 60;				// degrees
    if ( *h < 0 ) *h += 360;
}

/// Colors start======================================
TCOD_color_t TCOD_black={0,0,0};                   ///
TCOD_color_t TCOD_dark_grey={96,96,96};            ///
TCOD_color_t TCOD_grey={196,196,196};              ///
TCOD_color_t TCOD_white={255,255,255};             ///
TCOD_color_t TCOD_dark_blue={40,40,128};           ///
TCOD_color_t TCOD_light_blue={120,120,255};        ///
TCOD_color_t TCOD_dark_red={128,0,0};              ///
TCOD_color_t TCOD_light_red={255,100,50};          ///
TCOD_color_t TCOD_dark_brown={32,16,0};            ///
TCOD_color_t TCOD_light_yellow={255,255,150};      ///
TCOD_color_t TCOD_yellow={255,255,0};              ///
TCOD_color_t TCOD_dark_yellow={164,164,0};         ///
TCOD_color_t TCOD_green={0,220,0};                 ///
TCOD_color_t TCOD_orange={255,150,0};              ///
TCOD_color_t TCOD_red={255,0,0};                   ///
TCOD_color_t TCOD_silver={203,203,203};            ///
TCOD_color_t TCOD_gold={255,255,102};              ///
TCOD_color_t TCOD_purple={204,51,153};             ///
TCOD_color_t TCOD_dark_purple={51,0,51};           ///
/// Colors end========================================






/// DLL MAIN
HINSTANCE g_hInst;

extern (C) {
    void gc_init();
    void gc_term();
    void _minit();
    void _moduleCtor();
    void _moduleUnitTests();
}

version(Windows) {
    extern (Windows)
        BOOL DllMain(HINSTANCE hInstance, ULONG ulReason, LPVOID pvReserved) {
        switch (ulReason) {
        case DLL_PROCESS_ATTACH:
            gc_init();
            _minit();
            _moduleCtor();
            _moduleUnitTests();
            break;

        case DLL_PROCESS_DETACH:
            gc_term();
            break;

        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
            // Multiple threads not supported yet
            return false;
        }
        g_hInst=hInstance;
        return true;
    }
}
